use std::string::String;

use anyhow::{anyhow, Result};

use rbatis::value::DateTimeNow;
use serde::Deserialize;
use serde::Serialize;
use magic_crypt::{new_magic_crypt, MagicCryptTrait};
use rbatis::DateTimeNative;

use serde::de::DeserializeOwned;


use crate::models::menu::{Menu, MenuVo};
use crate::models::role::{Role, RoleVo};
use crate::models::user::UserRoleMenuVo;
use crate::utils::redis_util::REDIS_UTIL;

const KEY_PR: &str = "abcdefjhigklmnopqrstuvwxyz1234567890";
const SIGN_KEY: &str = "abcdefghigklmnopqrstuvwxyz1234567890";
// key, 32位长度
const SECRET_KEY: &[u8; 13] = b"a.and.b.and.c";


pub struct Crypt;

impl Crypt {
    pub fn encrypt<T: Serialize + ?Sized>(obj: &T) -> Result<String> {
        let value = serde_json::to_string(obj)?;
        let sc = new_magic_crypt!(KEY_PR,256);
        let vec = sc.encrypt_to_bytes(&value);

        let encrypt_string = sc.encrypt_bytes_to_base64(&value);
        Ok(encrypt_string)
    }
    pub fn decrypt_string<T: Serialize + DeserializeOwned + ?Sized>(encrypt_string: String) -> Result<T> {
        let sc = new_magic_crypt!(KEY_PR,256);
        let result = sc.decrypt_base64_to_string(encrypt_string)?;
        let t: T = serde_json::from_str(result.as_str())?;
        Ok(t)
    }
}


#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub user_id: u64,
    pub user_name: String,
    pub roles: Vec<Role>,
    pub menus: Vec<Menu>,
    pub exp: usize,
    pub create_time: DateTimeNative,
}


impl Claims {
    pub fn new(user_id: u64, user_name: String, roles: Vec<Role>, menus: Vec<Menu>, express_time: usize) -> Self {
        Claims {
            user_id,
            user_name,
            roles,
            menus,
            exp: express_time,
            create_time: DateTimeNative::now(),
        }
    }

    pub fn encode(&self) -> Result<String> {
        Crypt::encrypt(&self)
    }


    pub fn decode(token: String) -> Result<Self> {
        let result = Crypt::decrypt_string::<Claims>(token)?;
        Ok(result)
    }


    pub fn validation_token(access_token: &str) -> Result<Claims, anyhow::Error> {
        let claims = Self::decode(String::from(access_token))?;
        let current_timestamp = DateTimeNative::now().timestamp();
        println!("current time {:?}", current_timestamp);
        println!("express time {:?}", claims.exp as i64 + claims.create_time.timestamp() as i64);
        if current_timestamp > claims.exp as i64 + claims.create_time.timestamp() as i64 {
            return Err(anyhow!("token 无效"));
        }
        Ok(claims)
    }


    ///获取用户信息
    async fn get_user_info_by_access_token(access_token: &str) -> Result<UserRoleMenuVo> {
        let x = REDIS_UTIL.get_string(access_token).await?;
        let claims = Self::decode(x)?;
        Ok(UserRoleMenuVo::from(claims))
    }
}


impl From<Claims> for UserRoleMenuVo {
    fn from(claims: Claims) -> Self {
        Self {
            user_id: Some(claims.user_id),
            user_name: Some(claims.user_name),
            access_token: None,
            roles: Some(claims.roles),
            menus: Some(claims.menus),
        }
    }
}




